package main

import (
	"archive/zip"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"gitee.com/shift0ogg/xmind-counter/internal"
)

func fileExists(filename string) bool {
	info, err := os.Stat(filename)
	if os.IsNotExist(err) {
		return false
	}
	return !info.IsDir() // 确保不是目录
}

var (
	inputFile  string
	outputFile string
	help       bool
	delNum     bool
	Version    string
	CommitID   string
	BuildTime  string
)

func main() {
	// 输入和输出文件路径

	flag.StringVar(&inputFile, "i", "", "输入XMind文件路径")
	flag.StringVar(&outputFile, "o", "", "输出XMind文件路径")
	flag.BoolVar(&help, "h", false, "显示帮助信息")
	flag.BoolVar(&delNum, "d", false, "是否去掉[d]")
	versionFlag := flag.Bool("version", false, "Print version information")
	flag.Parse()

	if *versionFlag {
		fmt.Printf("Version: %s\nCommit: %s\nBuild Time: %s\n", Version, CommitID, BuildTime)
		os.Exit(0)
	}

	// 2. 处理帮助信息
	if help || inputFile == "" || outputFile == "" {
		fmt.Println("usage :")
		fmt.Println("  xmind-counter -i <input file> -o <output file>")
		fmt.Println("  for example:  xmind-counter -i ii.xmind -o oo.xmind")
		fmt.Println("args:")
		flag.PrintDefaults()
		return
	}

	if fileExists(inputFile) {
	} else {
		fmt.Println("input File does not exist.")
		return
	}

	// 1. 解压XMind文件
	tempDir, err := unzipXMind(inputFile)
	if err != nil {
		fmt.Printf("处理 XMind文件失败: %v\n", err)
		return
	}
	defer os.RemoveAll(tempDir)

	// 2. 读取并解析content.json
	contentPath := filepath.Join(tempDir, "content.json")
	content, err := os.ReadFile(contentPath)
	if err != nil {
		fmt.Printf("读取content.json失败: %v\n", err)
		return
	}

	var roots []internal.RootElement
	err = json.Unmarshal(content, &roots)
	if err != nil {
		fmt.Printf("解析content.json失败: %v\n", err)
		return
	}

	// 3. 统计每个节点的子孙节点数量并更新标题
	for i := range roots {
		processNode(&roots[i].RootTopic)
	}

	// 4. 将修改后的内容写回content.json
	updatedContent, err := json.MarshalIndent(roots, "", "  ")
	if err != nil {
		fmt.Printf("序列化修改后的content.json失败: %v\n", err)
		return
	}

	err = os.WriteFile(contentPath, updatedContent, 0644)
	if err != nil {
		fmt.Printf("写入修改后的content.json失败: %v\n", err)
		return
	}

	// 5. 重新打包为新的XMind文件
	err = zipXMind(tempDir, outputFile)
	if err != nil {
		fmt.Printf("创建新的XMind文件失败: %v\n", err)
		return
	}

	fmt.Printf("处理完成，已保存为: %s\n", outputFile)

}

// 解压XMind文件到临时目录
func unzipXMind(src string) (string, error) {
	tempDir, err := os.MkdirTemp("", "xmind")
	if err != nil {
		return "", err
	}

	r, err := zip.OpenReader(src)
	if err != nil {
		return "", err
	}
	defer r.Close()

	for _, f := range r.File {
		path := filepath.Join(tempDir, f.Name)

		if f.FileInfo().IsDir() {
			os.MkdirAll(path, os.ModePerm)
			continue
		}

		if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
			return "", err
		}

		outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return "", err
		}

		rc, err := f.Open()
		if err != nil {
			outFile.Close()
			return "", err
		}

		_, err = io.Copy(outFile, rc)
		outFile.Close()
		rc.Close()

		if err != nil {
			return "", err
		}
	}

	return tempDir, nil
}
func removeBrackets(s string) string {
	var result strings.Builder
	inBracket := false

	for _, char := range s {
		if char == '[' {
			inBracket = true
			continue
		}
		if char == ']' {
			inBracket = false
			continue
		}
		if !inBracket {
			result.WriteRune(char)
		}
	}
	return result.String()
}

// 处理节点，统计子孙节点数量并更新标题
func processNode(node *internal.Node) int {
	total := 0

	// 递归处理所有子节点
	//上一级节点形状是方框并且填充色是无色，则本节点是填充色为桃红色，本节点下面的所有子节点填充颜色设为蓝色；
	for i := range node.Children.Attached {
		//上一级节点形状是方框并且填充色桃红色，本节点下面的所有子节点填充颜色设为蓝色；
		if node.Style.Properties.Svg_fill == "#8EDDF9" {
			node.Children.Attached[i].Style.Properties.Svg_fill = "#8EDDF9"
			node.Children.Attached[i].Style.Properties.Fill_pattern = "solid"
		}
		if strings.Contains(node.Style.Properties.Shape_class, "ellipse") && node.Style.Properties.Svg_fill == "#F5E0F1" {
			node.Children.Attached[i].Style.Properties.Svg_fill = "#8EDDF9"
			node.Children.Attached[i].Style.Properties.Fill_pattern = "solid"
		}
		//上一级节点形状是方框并且填充色是无色，本节点是椭圆，则本节点是填充色为桃红色
		if strings.Contains(node.Style.Properties.Shape_class, "rect") &&
			strings.Contains(node.Children.Attached[i].Style.Properties.Shape_class, "ellipse") &&
			(node.Style.Properties.Fill_pattern == "none" || strings.Contains(node.Style.Properties.Svg_fill, "#FFFFFF")) {
			node.Children.Attached[i].Style.Properties.Svg_fill = "#F5E0F1"
			node.Children.Attached[i].Style.Properties.Fill_pattern = "solid"
			//fmt.Print(node.Children.Attached[i].Title)
		}
		total += processNode(&node.Children.Attached[i]) + 1
	}

	// 更新节点标题，添加计数

	if strings.Contains(node.Style.Properties.Shape_class, "rect") { //节点形状如果是方框，则将该节点的字体改为10号字；
		node.Style.Properties.Fo_font_size = "10"
	}
	if strings.Contains(node.Style.Properties.Shape_class, "ellipse") { //节点形状如果是椭圆，则将该节点的字体改为8号字
		node.Style.Properties.Fo_font_size = "8"
	}

	//先去掉[...]
	node.Title = removeBrackets(node.Title)

	//4、节点中的文字如果小于三行，则在最前面增加换行符号，让它成为三行文字；
	for len(strings.Split(node.Title, "\n")) <= 2 {
		node.Title = "\n" + node.Title
	}
	//节点中的文字如果大于三行，则从后面依次删减换行符，让它成为三行文字；

	dd := strings.Split(node.Title, "\n")
	if len(dd) > 2 {
		node.Title = strings.Join(dd[0:3], "\n") + strings.Join(dd[3:], "")
	}

	//检查是否已经添加过计数 每个节点（包括尾端节点）增第四行文字“[d]“,d是该节点子孙节点的个数，文字居中。
	if !delNum {
		node.Title = fmt.Sprintf("%s\n[%d]", node.Title, total)
	}
	node.Style.Properties.Fo_text_align = "center"

	return total
}

// 将临时目录打包为XMind文件
func zipXMind(src string, dest string) error {
	zipFile, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer zipFile.Close()

	zipWriter := zip.NewWriter(zipFile)
	defer zipWriter.Close()

	err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		relPath, err := filepath.Rel(src, path)
		if err != nil {
			return err
		}

		if info.IsDir() {
			_, err = zipWriter.Create(relPath + "/")
			return err
		}

		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		zipEntry, err := zipWriter.Create(relPath)
		if err != nil {
			return err
		}

		_, err = io.Copy(zipEntry, file)
		return err
	})

	return err
}
